# -*-coding:utf-8-*-
import os
import ctypes
import sys
import platform
from enum import Enum


class ColoredConsole:
    """ Console with color """

    class Mode(Enum):
        Default = 0
        Highlight = 1
        Underline = 2
        Flicker = 3
        Inverse = 4
        Invisible = 5
        Leftline = 6
        Rightline = 7
        Topline = 8

    class Color(Enum):
        White = 0
        Black = 1
        Red = 2
        Green = 3
        Blue = 4
        Yellow = 5
        Magenta = 6
        Cyan = 7

    def __init__(self):
        pass

    @staticmethod
    def print(*args, mode: Mode = None, fg: Color = None, bg: Color = None, sep=' ', end='\n', flush=False):
        """
        Print something with color
        :param args: data
        :param mode: display mode
        :param fg: foreground color
        :param bg: background color
        """
        win_cmd = False
        win_handle_out = None
        win_need_back = False
        if "Windows" in platform.system():
            win_handle_out = ctypes.windll.kernel32.GetStdHandle(-11)
            win_cmd = bool(ctypes.windll.kernel32.SetConsoleTextAttribute(win_handle_out, 0x07))

        cnt = len(args)
        if cnt > 0 and (mode is not None or fg is not None or bg is not None):
            if win_cmd and win_handle_out is not None:
                array_mode = (0x00, 0x08, 0x8000, 0x00, 0x4000, 0x00, 0x0800, 0x1000, 0x0400)
                array_fg = (0x07, 0x00, 0x04, 0x02, 0x01, 0x06, 0x05, 0x03)
                array_bg = (0x70, 0x00, 0x40, 0x20, 0x10, 0x60, 0x50, 0x30)
                att = 0
                if mode is not None and isinstance(mode, ColoredConsole.Mode):
                    att |= array_mode[mode.value]
                if fg is not None and isinstance(fg, ColoredConsole.Color):
                    att |= array_fg[fg.value]
                else:
                    att |= array_fg[0]
                if bg is not None and isinstance(bg, ColoredConsole.Color):
                    att |= array_bg[bg.value]
                else:
                    att |= array_bg[1]
                ctypes.windll.kernel32.SetConsoleTextAttribute(win_handle_out, att)
                win_need_back = True
            else:
                array_mode = (0, 1, 4, 5, 7, 8, 0, 0, 0)
                array_fg = (37, 30, 31, 32, 34, 33, 35, 36)
                array_bg = (47, 40, 41, 42, 44, 43, 45, 46)
                clr = "\033["  # \033[mode;fg;bg m
                if mode is not None and isinstance(mode, ColoredConsole.Mode):
                    clr += str(array_mode[mode.value]) + ';'
                if fg is not None and isinstance(fg, ColoredConsole.Color):
                    clr += str(array_fg[fg.value]) + ';'
                if bg is not None and isinstance(bg, ColoredConsole.Color):
                    clr += str(array_bg[bg.value]) + ';'
                clr = (clr[:-1] if clr.endswith(';') else clr) + 'm'
                if len(clr) != 3:
                    lst = list(args)
                    lst[0] = clr + str(lst[0])
                    lst[cnt - 1] = str(lst[cnt - 1]) + "\033[0m"
                    args = tuple(lst)
        print(*args, sep=sep, end=end, flush=flush)
        if win_need_back:
            ctypes.windll.kernel32.SetConsoleTextAttribute(win_handle_out, 0x07)
        pass


ColoredPrint = ColoredConsole.print
ConsoleMode = ColoredConsole.Mode
ConsoleColor = ColoredConsole.Color


def clear_console():
    if "Windows" in platform.system():
        os.system("cls > nul")
    else:
        os.system("clear > /dev/null")
    pass


def input_verify(prompt: str, verify_func=None, default=None, error_prompt: str = None):
    from types import FunctionType
    if verify_func is None and not (
            callable(verify_func) or isinstance(verify_func, FunctionType) or hasattr(verify_func, '__call__')):
        return input(prompt)
    # else
    while True:
        str = input(prompt)
        if verify_func(str):
            return str
        # else not return
        elif error_prompt is not None:
            ColoredConsole.print(error_prompt,
                                 mode=ColoredConsole.Mode.Highlight,
                                 fg=ColoredConsole.Color.Red)
        elif default is not None:
            return default
        else:
            return None


input_str = input_verify


def input_int(prompt: str, verify_func=None, default: int = None, error_prompt: str = None):
    ver_func = lambda x: str(x).isdigit()
    if verify_func is not None:
        ver_func = lambda x: str(x).isdigit() and verify_func(int(x))
    ret = input_verify(prompt, verify_func=ver_func, default=default, error_prompt=error_prompt)
    if isinstance(ret, str):
        return int(ret)
    else:
        return ret


def input_alphanum(prompt: str, verify_func=None, default: int = None, error_prompt: str = None):
    ver_func = lambda x: str(x).isalnum()
    if verify_func is not None:
        ver_func = lambda x: str(x).isalnum() and verify_func(int(x))
    return input_verify(prompt, verify_func=ver_func, default=default, error_prompt=error_prompt)


def exit_console_script():
    import platform
    if platform.system() == "Windows":
        input("按回车键退出...")
    sys.exit(0)


if __name__ == "__main__":
    ColoredConsole.print("Should cleared.")
    clear_console()
    ColoredConsole.print("ABC", 32, mode=ColoredConsole.Mode.Highlight, fg=ColoredConsole.Color.Green,
                         sep=" ", end="\n", flush=False)
    ColoredConsole.print("ABC", True, mode=ColoredConsole.Mode.Underline)
    ColoredConsole.print("ABC", mode=ColoredConsole.Mode.Rightline)
    input()
